package com.pig4cloud.pig.common.security.feign;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.core.util.WebUtils;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;

import java.util.Collection;

/**
 * OAuth2 Feign请求拦截器
 * <p>
 * 实现Feign调用时OAuth2令牌的自动传递，重写了Spring官方的OAuth2FeignRequestInterceptor，
 * 解决了官方实现在某些场景下不适用的问题
 * </p>
 *
 * @author lengleng
 * @date 2022/5/29
 */
@Slf4j
@RequiredArgsConstructor
public class PigOAuthRequestInterceptor implements RequestInterceptor {

	/**
	 * 用于解析Bearer令牌的解析器
	 */
	private final BearerTokenResolver tokenResolver;

	/**
	 * 应用请求拦截
	 * <p>
	 * 在Feign请求发送前，自动添加OAuth2认证头。
	 * 处理逻辑：
	 * 1. 如果请求已经带有FROM头且值为FROM_IN，则跳过处理（内部调用）
	 * 2. 如果不是Web请求环境，则跳过处理
	 * 3. 从当前请求中解析Bearer Token并添加到Feign请求头中
	 * </p>
	 * @param template Feign请求模板
	 */
	@Override
	public void apply(RequestTemplate template) {
		Collection<String> fromHeader = template.headers().get(SecurityConstants.FROM);
		// 带from 请求直接跳过
		if (CollUtil.isNotEmpty(fromHeader) && fromHeader.contains(SecurityConstants.FROM_IN)) {
			return;
		}

		// 非web 请求直接跳过
		if (!WebUtils.getRequest().isPresent()) {
			return;
		}
		HttpServletRequest request = WebUtils.getRequest().get();
		// 避免请求参数的 query token 无法传递
		String token = tokenResolver.resolve(request);
		if (StrUtil.isBlank(token)) {
			return;
		}
		template.header(HttpHeaders.AUTHORIZATION,
				String.format("%s %s", OAuth2AccessToken.TokenType.BEARER.getValue(), token));

	}

}
